package ru.reaperoq.utils

import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.util.pipeline.*
import kotlinx.coroutines.Dispatchers
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import ru.reaperoq.database.Database
import ru.reaperoq.dto.respondError
import ru.reaperoq.schema.User
import ru.reaperoq.schema.UserRole
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId

suspend fun <T> dbQuery(block: suspend () -> T): T =
    newSuspendedTransaction(Dispatchers.IO) { block() }

suspend inline fun <reified T : @Serializable Any> ApplicationCall.getBody(): T? {
    try {
        return this.receive()
    } catch (e: Exception) {
        e.printStackTrace()
        respondError("Ошибка при десериализации тела запроса")
        return null
    }
}

suspend fun PipelineContext<Unit, ApplicationCall>.getUser(): User? {
    return call.principal<UserIdPrincipal>()?.name?.toInt()?.let {
        Database.userService.read(it)
    } ?: run {
        call.respondError("Пользователь не найден", HttpStatusCode.NotFound)
        null
    }
}

suspend fun PipelineContext<Unit, ApplicationCall>.withRole(vararg roles: UserRole, block: suspend () -> Unit) {
    val userRole = call.principal<UserIdPrincipal>()?.name?.let { Database.userService.read(it.toInt())?.role }
    if (userRole in roles) {
        block()
    } else {
        call.respondError("Недостаточно прав", HttpStatusCode.Forbidden)
    }
}

fun LocalDateTime.toEpochMillis(): Long = this.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()

fun Long.toLocalDateTime(): LocalDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault())